package com.eian.boot.json.handler;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;

/**
 * 自定义时间类型反序列化处理器（支持多种格式和Java 8时间API）
 * 同时兼容传统Date类型
 *
 * @author alex.meng
 */
public class CustomDateDeserializer extends JsonDeserializer<Date> {

    // 支持的各种日期时间格式与类型的映射
    private static final java.util.Map<DateTimeFormatter, FormatType> FORMATTERS = new java.util.HashMap<>();
    // *** 指定东八区时区 ***
    private static final ZoneId TARGET_ZONE_ID = ZoneId.of("Asia/Shanghai");

    static {
        // 使用静态代码块初始化格式器映射
        FORMATTERS.put(DateTimeFormatter.ISO_LOCAL_DATE_TIME, FormatType.LOCAL_DATE_TIME); // Treat as LocalDateTime
        FORMATTERS.put(DateTimeFormatter.ISO_OFFSET_DATE_TIME, FormatType.OFFSET_DATE_TIME);
        FORMATTERS.put(DateTimeFormatter.ISO_ZONED_DATE_TIME, FormatType.ZONED_DATE_TIME);
        FORMATTERS.put(DateTimeFormatter.ISO_INSTANT, FormatType.INSTANT);
        FORMATTERS.put(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), FormatType.LOCAL_DATE_TIME); // Treat as LocalDateTime
        FORMATTERS.put(DateTimeFormatter.ofPattern("yyyy-MM-dd"), FormatType.LOCAL_DATE);
        FORMATTERS.put(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"), FormatType.LOCAL_DATE_TIME); // Treat as LocalDateTime
        FORMATTERS.put(DateTimeFormatter.ofPattern("yyyy/MM/dd"), FormatType.LOCAL_DATE);
        FORMATTERS.put(DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", java.util.Locale.US), FormatType.JS_DATE_STRING); // JavaScript Date.toString()
    }

    /**
     * 将字符串解析为Instant对象
     * <p>
     * 对于没有明确时区信息的日期时间（如 LocalDateTime, LocalDate），
     * 将使用指定的东八区 (TARGET_ZONE_ID) 进行解释。
     * </p>
     */
    private Instant parseToInstant(String text) throws DateTimeParseException {
        // 优先尝试解析为毫秒时间戳，提高效率
        // 时间戳是相对于 UTC 的，不受 TARGET_ZONE_ID 影响
        try {
            long epochMillis = Long.parseLong(text);
            return Instant.ofEpochMilli(epochMillis);
        } catch (NumberFormatException ignored) {
            // 如果不是数字，继续尝试其他格式
        }

        // 尝试所有支持的格式
        for (java.util.Map.Entry<DateTimeFormatter, FormatType> entry : FORMATTERS.entrySet()) {
            DateTimeFormatter formatter = entry.getKey();
            FormatType type = entry.getValue();
            try {
                switch (type) {
                    case OFFSET_DATE_TIME:
                        // 带偏移量的日期时间，直接解析为 Instant
                        OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
                        return odt.toInstant();
                    case ZONED_DATE_TIME:
                        // 带时区的日期时间，直接解析为 Instant
                        ZonedDateTime zdt = ZonedDateTime.parse(text, formatter);
                        return zdt.toInstant();
                    case INSTANT:
                        // ISO_INSTANT 格式，直接解析为 Instant
                        return Instant.parse(text);
                    case LOCAL_DATE_TIME:
                        // *** 使用东八区解释 LocalDateTime ***
                        LocalDateTime ldt = LocalDateTime.parse(text, formatter);
                        return ldt.atZone(TARGET_ZONE_ID).toInstant();
                    case LOCAL_DATE:
                        // *** 使用东八区解释 LocalDate (作为当天 00:00:00) ***
                        LocalDate ld = LocalDate.parse(text, formatter);
                        return ld.atStartOfDay(TARGET_ZONE_ID).toInstant();
                    case JS_DATE_STRING:
                        // JavaScript Date.toString() 通常包含时区信息，直接解析为 Instant
                        ZonedDateTime jsZdt = ZonedDateTime.parse(text, formatter);
                        return jsZdt.toInstant();
                    default:
                        // 理论上不会到达这里，因为枚举已覆盖所有情况
                        break;
                }
            } catch (DateTimeParseException ignored) {
                // 尝试下一个格式
            }
        }

        // 如果所有格式都失败，抛出异常
        throw new DateTimeParseException("Text '" + text + "' could not be parsed as date", text, 0);
    }

    /**
     * 反序列化逻辑：将字符串转换为 Date 对象
     *
     * @param p    JSON 解析器，用于获取字符串值
     * @param ctxt 上下文环境
     * @return 转换后的 Date 对象，若为空字符串返回 null
     * @throws IOException 当字符串格式非法或转换失败时抛出
     */
    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String text = p.getText().trim();
        if (text.isEmpty()) {
            return null;
        }

        try {
            // 尝试解析为Instant
            Instant instant = parseToInstant(text);
            return Date.from(instant);
        } catch (DateTimeParseException e) {
            throw new IOException("Failed to parse date: " + text, e);
        }
    }

    // 定义支持的各种日期时间格式及其类型
    private enum FormatType {
        LOCAL_DATE_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME, INSTANT, LOCAL_DATE, JS_DATE_STRING, TIMESTAMP
    }
}